﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Inspire.DataStructures;
using Inspire.Entities;
using Inspire.Utility;
using Inspire.UIResources;
using System.Data;
using System.Data.Objects;
using System.Windows.Threading;
using System.ComponentModel;
using Inspire.Classes.Business;
using System;
using Inspire.Classes.Interfaces;
using Inspire.Classes.Common;
using Microsoft.Win32;
using System.IO;
using System.Drawing;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Drawing.Imaging;
using Inspire.Classes.Utility;

namespace Inspire.Pages
{
    /// <summary>
    /// Interaction logic for AddPatrons.xaml
    /// </summary>
    public partial class AddResources : Page, IPage
    {
        /// <summary>
        /// Get a reference to the PatronBusiness object.
        /// </summary>
        private ResourceBusiness _resourceBusiness = ResourceBusiness.GetResourceBusiness();

        /// <summary>
        /// Constructor
        /// </summary>
        public AddResources()
        {
            InitializeComponent();
            Reset();
        }

        /// <summary>
        /// Initialize lists that back drop-downs.
        /// </summary>
        private void InitializeLists()
        {
            CustomFieldBusiness customFieldBusiness = CustomFieldBusiness.GetCustomFieldBusiness();
            Resource nullResource = new Resource();
            nullResource.Id = -1;
            nullResource.Name = "[ No Parent ]";

            // Clear the lists
            this._customFields.Clear();
            this._allResources.Clear();

            try
            {
                // Create the Custom Field Types structures list for the user to fill in
                foreach (CustomFieldType nextField in customFieldBusiness.GetCustomFieldTypesByCategory(Constants.CUSTOM_FIELD_RESOURCE_CUSTOM))
                {
                    NewCustomFieldSource newSource = new NewCustomFieldSource();
                    newSource.CustomFieldType = nextField;

                    this._customFields.Add(newSource);
                }

                // Create the AllResource list
                this._allResources.Add(nullResource);
                foreach (Resource nextResource in _resourceBusiness.GetAllResources())
                {
                    this._allResources.Add(nextResource);
                }
            }
            catch (Exception e)
            {
                Console.Out.WriteLine(e.Message);
                Console.Out.WriteLine(e.StackTrace);
            }
        }

        #region Basic Tab Fields

        // New Resource to Populate with Information
        private Resource _newResource = new Resource();

        /// <summary>
        /// The New Patron Entity to be created and persisted
        /// </summary>
        public Resource NewResource
        {
            get
            {
                return this._newResource;
            }
            set
            {
                this._newResource = value;
            }
        }

        /// <summary>
        /// List containing all resources to back the Parent Resource list.
        /// </summary>
        private ObservableCollection<Resource> _allResources = new ObservableCollection<Resource>();
        public ObservableCollection<Resource> AllResources
        {
            get
            {
                return this._allResources;
            }
        }

        #endregion

        #region Custom Tab Fields

        /// <summary>
        /// The Custom Field Type data structures for the Custom tab
        /// </summary>
        private ObservableCollection<NewCustomFieldSource> _customFields = new ObservableCollection<NewCustomFieldSource>();
        public ObservableCollection<NewCustomFieldSource> CustomFields
        {
            get
            {
                return this._customFields;
            }
            set
            {
                this._customFields = value;
            }
        }

        #endregion

        #region Event Handlers and General Methods

        /// <summary>
        /// Handle a button click for the Save button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, RoutedEventArgs e)
        {
            #region Validation
            // Validate all fields, even if unchanged
            if (!(errorProvider1.ValidateFinal()))
            {
                // Show a MessageBox with all errors
                string output = "";
                foreach (string error in this.errorProvider1.ErrorList)
                {
                    output += error + "\n";
                }

                // Display the Errors
                System.Windows.MessageBox.Show(output, "Validation Error", MessageBoxButton.OK, MessageBoxImage.Error);

                // Give Focus to the First Erroneous Field
                errorProvider1.FirstInvalidElement.Focus();

                // Exit this method to stop the save
                return;
            }
            #endregion

            #region Saving

            // If the Parent Resource field is set to -1 ([ No Parent ]), set ParentId to null
            if (this.NewResource.ParentResource.Id == -1)
            {
                this.NewResource.ParentResource = null;
            }

            // Ensure a new Patron is returned. If not, alert the user.
            if (this._resourceBusiness.AddResource(this.NewResource, this.CustomFields) == null)
            {
                System.Windows.MessageBox.Show("An error occurred when saving. Please try saving again.", "Error.", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            dgCustomFields.Items.Refresh();

            #endregion

            #region Resetting
            
            // Forward the Reset  to the Reset() method
            this.NewResource = new Resource();
            this.errorProvider1.DataContext = this.NewResource;
            this.txtNotes.DataContext = this.NewResource;
            this.Reset();

            #endregion
        }

        /// <summary>
        /// Handle the Cancel button being clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            // Forward the Reset command to the Reset() method
            this.Reset();
        }

        /// <summary>
        /// If a cell is selected in our custom fields grid, activate it for editing.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dgCustomFields_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
        {
            if (e.AddedCells.Count < 1) return;
            if (e.AddedCells[0].Column.DisplayIndex == 1)
            {
                dgCustomFields.BeginEdit();
            }
        }

        /// <summary>
        /// Handles switching tabs if user uses the nav buttons at the bottom of the screen
        /// </summary>
        /// <param name="sender">(Button) oject sending the event</param>
        /// <param name="e">(RoutedEventArgs) Arguments from the event</param>
        private void NavigateButtons_OnClick(object sender, RoutedEventArgs e)
        {
            // Handle Changing Tabs for Previous Button
            if (((Button)sender).Name.Equals("btnPrevious"))
            {
                // If it's already at the leftmost tab, do nothing
                if (this.tabControlAddResources.SelectedIndex == 0)
                {
                    return;
                }
                else
                {
                    // If the first tab is about to be shown, disable the Previous button
                    if (this.tabControlAddResources.SelectedIndex == 1)
                    {
                        this.btnPrevious.IsEnabled = false;

                        // Adjust the Text of the Previous Button
                        this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex - 1]).Header.ToString();
                    }
                    else
                    {
                        // Adjust the Text of the Previous Button
                        this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex - 2]).Header.ToString();
                    }

                    // Adjust the Text of the Next Button
                    this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex]).Header.ToString();

                    // Adjust the Visible Tab
                    this.tabControlAddResources.SelectedIndex = this.tabControlAddResources.SelectedIndex - 1;

                    // Ensure the Next Button is Enabled
                    this.btnNext.IsEnabled = true;
                }
            }

            // Handle Changing Tabs for the Next Button
            if (((Button)sender).Name.Equals("btnNext"))
            {
                // If it's already at the rightmost tab, do nothing
                if (this.tabControlAddResources.SelectedIndex == this.tabControlAddResources.Items.Count - 1)
                {
                    return;
                }
                else
                {
                    // If the last tab is about to be shown, disable the Next button
                    if (this.tabControlAddResources.SelectedIndex == (this.tabControlAddResources.Items.Count - 2))
                    {
                        this.btnNext.IsEnabled = false;

                        // Adjust the Text of the Next Button
                        this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex + 1]).Header.ToString();
                    }
                    else
                    {
                        // Adjust the Text of the Next Button
                        this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex + 2]).Header.ToString();
                    }

                    // Adjust the Text of the Previous Button
                    this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex]).Header.ToString();

                    // Adjust the Visible Tab
                    this.tabControlAddResources.SelectedIndex = this.tabControlAddResources.SelectedIndex + 1;

                    // Ensure the Previous Button is Enabled
                    this.btnPrevious.IsEnabled = true;
                }
            }
        }

        /// <summary>
        /// Allows the user to select an image for either the Icon size of the Full size.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void ChangeImageButton_OnClick(object sender, RoutedEventArgs e)
        {
            OpenFileDialog      fileDialog = new OpenFileDialog();
            byte[]              fileContents = null,
                                resizedContents = null;
            MemoryStream        memoryStream = null,
                                memoryStream2 = null;
            Bitmap              original = null,
                                resized = null;
            Rectangle           rect1 = new Rectangle(),
                                rect2 = new Rectangle();
            BitmapData          bitmapData1 = null,
                                bitmapData2 = null;
            BitmapImage         imageSource = null;
            double              scaledSize = 0.0d;
            int                 stride = 0;

            // Prepare the dialog
            fileDialog.Filter = "Image Formats|*.bmp;*.jpg;*.gif;*.png";
            fileDialog.Title = "Select an Image...";

            // Display the dialog. If the user clicks cancel, return immediately.
            bool? fileDialogResult = fileDialog.ShowDialog();
            if (fileDialogResult == null || fileDialogResult == false)
            {
                return;
            }

            /*
             * So we have an image selected...let's go ahead and copy the entire file into a byte[]
             */
            // Create a FileInfo object so we can determine the size of the file and initialize our byte[]
            // We use FromStream instead of FromFile method because FromFile method sometimes does not release the file handle
            original = (Bitmap)Bitmap.FromStream(new MemoryStream(File.ReadAllBytes(fileDialog.FileName)), true);
            rect1 = new Rectangle(0, 0, original.Width, original.Height);
            bitmapData1 = original.LockBits(rect1, ImageLockMode.ReadOnly, original.PixelFormat);

            // Create a MemoryStream to the appropriate length
            memoryStream = new MemoryStream();
            memoryStream.SetLength(original.Height * bitmapData1.Stride * System.Drawing.Image.GetPixelFormatSize(original.PixelFormat) / 8);

            // Save the bytes of the image to the MemoryStream
            original.Save(memoryStream, original.RawFormat);
            memoryStream.Seek(0, SeekOrigin.Begin);

            // Write the bytes to a byte[]
            fileContents = memoryStream.ToArray();

            // Close the stream
            memoryStream.Close();
            memoryStream.Dispose();

            // Now that we have the new image, let's allocate some memory in the NewResource as a byte[] for this image, open a memory stream to that memory,
            // dump the new image into the NewResource's byte[], and point the Image control on the screen to this buffer for display
            if (((Button)sender).Name.Equals(btnEditFullImage.Name))
            {
                resizedContents = ImageProcessing.ResizeImage(fileContents, Constants.IMAGE_DIMENSION_FULL_MAX_WIDTH, Constants.IMAGE_DIMENSION_FULL_MAX_HEIGHT);
                this.NewResource.FullSizeImage = resizedContents.ToArray<Byte>();
            }
            else
            {
                resizedContents = ImageProcessing.ResizeImage(fileContents, Constants.IMAGE_DIMENSION_THUMB_MAX_WIDTH, Constants.IMAGE_DIMENSION_THUMB_MAX_HEIGHT);
                this.NewResource.IconSizeImage = resizedContents.ToArray<Byte>();
            }

            // Use a MemoryStream to read in the contents of the byte[]
            memoryStream2 = new MemoryStream(resizedContents);

            // Create a bitmap from the bytes
            resized = new Bitmap(memoryStream2);

            // Set the Position of the Stream to the beginning
            memoryStream2.Seek(0, SeekOrigin.Begin);

            // Create a new BitmapImage to use as the source of the image control
            imageSource = new BitmapImage();
            imageSource.BeginInit();
            imageSource.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
            imageSource.CacheOption = BitmapCacheOption.OnLoad;
            imageSource.StreamSource = memoryStream2;
            imageSource.DecodePixelWidth = resized.Width;
            imageSource.DecodePixelHeight = resized.Height;
            imageSource.EndInit();
            imageSource.Freeze();

            // Lock the new bitmap's bits to get a BitmapData object so we can get the stride
            rect2 = new Rectangle(0, 0, resized.Width, resized.Height);
            bitmapData2 = resized.LockBits(rect2, ImageLockMode.ReadOnly, resized.PixelFormat);

            // Set the Source, Height, and Width of the Image control
            if (((Button)sender).Name.Equals(btnEditFullImage.Name))
            {
                this.imgFullSize.Width = (double)bitmapData2.Width;
                this.imgFullSize.Height = (double)bitmapData2.Height;
                this.imgFullSize.Source = imageSource;
            }
            else
            {
                this.imgIconSize.Width = (double)bitmapData2.Width;
                this.imgIconSize.Height = (double)bitmapData2.Height;
                this.imgIconSize.Source = imageSource;
            }
        }

        /// <summary>
        /// Allows the user to clear a previously selected image for either the Icon sized or Full sized image.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeleteImageButton_Click(object sender, RoutedEventArgs e)
        {
            // Clear the Full-sized Image
            if (((Button)sender).Name.Equals(btnDeleteFullImage.Name))
            {
                this.NewResource.FullSizeImage = null;
                this.imgFullSize.Source = (ImageSource)FindResource("no_img_available");
                this.imgFullSize.Width = 180.0d;
                this.imgFullSize.Height = 131.0d;
            }
            // Clear the Icon-sized Image
            else
            {
                this.NewResource.IconSizeImage = null;
                this.imgIconSize.Source = (ImageSource)FindResource("no_img_available");
                this.imgIconSize.Width = 180.0d;
                this.imgIconSize.Height = 131.0d;
            }
        }

        /// <summary>
        /// Reset the fields
        /// </summary>
        private void Reset()
        {
            // Re-build the lists
            InitializeLists();

            // Reset the fields to clear them
            this.NewResource.Reset();

            // Set the NewResource's ParentResource to the first resource ([ No Parent ]) in the _allResources list
            this.NewResource.ParentResource = this._allResources[0];
            this.ddlParentResource.SelectedIndex = 0;

            // Call Validate() on the ErrorProvider so all Invalid markers are removed (being that this.NewResource.Reset() clears all error messages)
            this.errorProvider1.Validate();

            // Reset the custom fields
            foreach (NewCustomFieldSource nextEntry in this._customFields)
            {
                nextEntry.Reset();
            }

            // Reset the images
            this.imgFullSize.Source = (ImageSource)FindResource("no_img_available");
            this.imgFullSize.Width = 180.0d;
            this.imgFullSize.Height = 131.0d;

            this.imgIconSize.Source = (ImageSource)FindResource("no_img_available");
            this.imgIconSize.Width = 180.0d;
            this.imgIconSize.Height = 131.0d;

           // Move to the first tab
            this.tabControlAddResources.SelectedIndex = 0;
        }

        /// <summary>
        /// Handles updating the Forward and Backward Control Buttons when a new tab is selected
        /// on the TabControl.
        /// </summary>
        /// <param name="sender">(TabControl) object sending the event</param>
        /// <param name="e">(SelectionChangedEventArgs) Arguments from the event</param>
        private void tabControlAddResources_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // If it's the first Tab
            if (this.tabControlAddResources.SelectedIndex == 0)
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[0]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[1]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = false;
                this.btnNext.IsEnabled = true;
            }

            // If it's an in-between Tab
            if (this.tabControlAddResources.SelectedIndex > 0 && this.tabControlAddResources.SelectedIndex < (this.tabControlAddResources.Items.Count - 1))
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex - 1]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex + 1]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = true;
                this.btnNext.IsEnabled = true;
            }

            // If it's the last Tab
            if (this.tabControlAddResources.SelectedIndex == (this.tabControlAddResources.Items.Count - 1))
            {
                // Adjust the Text of the Next & Previous Buttons
                this.lblPreviousButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex - 1]).Header.ToString();
                this.lblNextButton.Text = ((TabItem)this.tabControlAddResources.Items[this.tabControlAddResources.SelectedIndex]).Header.ToString();

                // Adjust the IsEnabled of the Next & Previous Buttons
                this.btnPrevious.IsEnabled = true;
                this.btnNext.IsEnabled = false;
            }
        }

        #endregion

        #region IPage Members

        /// <summary>
        /// Notification when this pageIndex is displayed to the user.
        /// </summary>
        public void Page_Displayed()
        {
            /*
             * Refresh the custom field lists, in case they were edited.
             */
            CustomFieldBusiness customFieldBusiness = CustomFieldBusiness.GetCustomFieldBusiness();
            this._customFields.Clear();

            // Create the Custom Field Types structures list for the user to fill in
            foreach (CustomFieldType nextField in customFieldBusiness.GetCustomFieldTypesByCategory(Constants.CUSTOM_FIELD_RESOURCE_CUSTOM))
            {
                NewCustomFieldSource newSource = new NewCustomFieldSource();
                newSource.CustomFieldType = nextField;

                this._customFields.Add(newSource);
            }
        }

        #endregion
    }
}
